/*
 * @(#) $Header$
 *
 * Copyright (C)  2006  Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.baselib.util.function;

import ca.forklabs.baselib.util.UnaryFunction;

/**
 * Class {@code UnaryCompose} is an {@link UnaryFunction} that, given two
 * {@code UnaryFunction}s <em>f(y)</em> and <em>g(x)</em>, performs
 * <em>f(g(x))</em>.
 *
 * @param   <R>   the type of the returned object of <em>f(y)</em>.
 * @param   <I>   the type of the argument object of <em>f(y)</em>, and the
 *                type of the returned object of <em>g(x)</em>.
 * @param   <A>   the type of the argument object of <em>g(x)</em>.
 *
 * @author   <a href="mailto:forklabs at gmail.com?subject=ca.forklabs.baselib.util.function.UnaryCompose">Daniel Léonard</a>
 * @version $Revision$
 */
public class UnaryCompose<R, I, A> implements UnaryFunction<R, A> {

//---------------------------
// Instance variable
//---------------------------

   /** The function <code>f</code>. */
   private UnaryFunction<R, I> f;
   /** The function <code>g</code>. */
   private UnaryFunction<I, A> g;


//---------------------------
// Constructor
//---------------------------

   /**
    * Constructor.
    * @param   g   the function <em>g(x)</em>
    * @param   f   the function <em>f(y)</em>
    * @exception   NullPointerException   if any of the two function is
    *                                     {@code null}.
    */
   public UnaryCompose(UnaryFunction<R, I> f, UnaryFunction<I, A> g) {
      this.setF(f);
      this.setG(g);
      }


//---------------------------
// Accessor and mutator
//---------------------------

   /**
    * Changes the function <em>f(y)</em>.
    * @param   f   <em>f(y)</em>
    * @exception   NullPointerException   if the function is {@code null}.
    */
   @SuppressWarnings("nls")
   protected void setF(UnaryFunction<R, I> f) {
      if (null == f) {
         String message = this.getNullFunctionErrorMessage("f(y)");
         throw new NullPointerException(message);
         }
      this.f = f;
      }

   /**
    * Returns the function <em>f(y)</em>.
    * @return   <em>f(y)</em>
    */
   protected UnaryFunction<R, I> getF() {
      return this.f;
      }

   /**
    * Changes the function <em>g(x)</em>.
    * @param   g   <em>g(x)</em>
    * @exception   NullPointerException   if the function is {@code null}.
    */
   @SuppressWarnings("nls")
   protected void setG(UnaryFunction<I, A> g) {
      if (null == g) {
         String message = this.getNullFunctionErrorMessage("g(x)");
         throw new NullPointerException(message);
         }
      this.g = g;
      }

   /**
    * Returns the function <em>g()</em>.
    * @return   <em>g()</em>
    */
   protected UnaryFunction<I, A> getG() {
      return this.g;
      }


//---------------------------
// Implemented method from ca.forklabs.baselib.util.UnaryFunction
//---------------------------

   /**
    * Returns the result of <em>f(g(x))</em>.
    * @param  arg   the argument.
    * @return   <em>f(g(arg))</em>
    */
   @Override
   public R invoke(A arg) {
      UnaryFunction<I, A> g_of_x = this.getG();
      I intermediate = g_of_x.invoke(arg);

      UnaryFunction<R, I> f_of_y = this.getF();
      R result = f_of_y.invoke(intermediate);

      return result;
      }


//---------------------------
// Instance methods
//---------------------------

   /**
    * Gets the error message saying that a function is {@code null}.
    * @param   function   the null function.
    * @return   the error message.
    */
   protected String getNullFunctionErrorMessage(String function) {
      String key = Resources.COMPOSE_NULL_FUNCTION;
      String message = Resources.getLocalizedString(key, function);
      return message;
      }

   }
